package mrpanyu.guitool.crypt;

import java.security.KeyPair;

import org.bouncycastle.crypto.engines.SM2Engine.Mode;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import mrpanyu.guitool.base.annotation.Action;
import mrpanyu.guitool.base.annotation.OnParameterChange;
import mrpanyu.guitool.base.annotation.Parameter;
import mrpanyu.guitool.base.annotation.ToolModel;
import mrpanyu.guitool.base.model.ParameterType;
import mrpanyu.guitool.base.model.Tool;

@ToolModel(displayName = "国密SM2非对称加密")
public class Sm2Tool {

	@Parameter(displayName = "操作", type = ParameterType.SELECT, options = { "Encrypt", "Decrypt", "Sign",
			"VerifySign" }, order = 1)
	private String operation = "Encrypt";

	@Parameter(displayName = "模式", type = ParameterType.SELECT, options = { "C1C3C2", "C1C2C3" }, order = 2)
	private String mode = "C1C3C2";

	@Parameter(displayName = "私钥", description = "支持Hex或Base64格式", order = 3, visible = false)
	private String privateKey;

	@Parameter(displayName = "公钥", description = "支持Hex或Base64格式", order = 4)
	private String publicKey;

	@Parameter(displayName = "原始内容", type = ParameterType.MULTILINE_TEXT, order = 5)
	private String content;

	@Parameter(displayName = "加密文本", type = ParameterType.MULTILINE_TEXT_LINEWRAP, description = "支持Hex或Base64格式，注意如果是前端js sm-crypto加密出来的Hex字符串，需要手工添加04前缀后才能在java中解密。同样java生成的密文Hex要去掉04前缀后才能在js sm-crypto库里面解密", order = 6, visible = false)
	private String encryptedContent;

	@Parameter(displayName = "签名", description = "支持Hex或Base64格式", order = 7, visible = false)
	private String signature;

	@OnParameterChange("operation")
	public void onOperationChange(Tool tool) {
		if ("Encrypt".equals(operation)) {
			tool.getParameter("privateKey").setVisible(false);
			tool.getParameter("publicKey").setVisible(true);
			tool.getParameter("content").setVisible(true);
			tool.getParameter("encryptedContent").setVisible(false);
			tool.getParameter("signature").setVisible(false);
			tool.getAction("encrypt").setVisible(true);
			tool.getAction("decrypt").setVisible(false);
			tool.getAction("sign").setVisible(false);
			tool.getAction("verifySign").setVisible(false);
		} else if ("Decrypt".equals(operation)) {
			tool.getParameter("privateKey").setVisible(true);
			tool.getParameter("publicKey").setVisible(false);
			tool.getParameter("content").setVisible(false);
			tool.getParameter("encryptedContent").setVisible(true);
			tool.getParameter("signature").setVisible(false);
			tool.getAction("encrypt").setVisible(false);
			tool.getAction("decrypt").setVisible(true);
			tool.getAction("sign").setVisible(false);
			tool.getAction("verifySign").setVisible(false);
		} else if ("Sign".equals(operation)) {
			tool.getParameter("privateKey").setVisible(true);
			tool.getParameter("publicKey").setVisible(false);
			tool.getParameter("content").setVisible(true);
			tool.getParameter("encryptedContent").setVisible(false);
			tool.getParameter("signature").setVisible(false);
			tool.getAction("encrypt").setVisible(false);
			tool.getAction("decrypt").setVisible(false);
			tool.getAction("sign").setVisible(true);
			tool.getAction("verifySign").setVisible(false);
		} else {
			tool.getParameter("privateKey").setVisible(false);
			tool.getParameter("publicKey").setVisible(true);
			tool.getParameter("content").setVisible(true);
			tool.getParameter("encryptedContent").setVisible(false);
			tool.getParameter("signature").setVisible(true);
			tool.getAction("encrypt").setVisible(false);
			tool.getAction("decrypt").setVisible(false);
			tool.getAction("sign").setVisible(false);
			tool.getAction("verifySign").setVisible(true);
		}
	}

	@Action(displayName = "加密", order = 1)
	public void encrypt(Tool tool) throws Exception {
		tool.clearMessages();
		SM2 sm2 = new SM2(null, publicKey);
		setMode(sm2);
		String encHex = sm2.encryptHex(content, KeyType.PublicKey);
		String encBase64 = sm2.encryptBase64(content, KeyType.PublicKey);
		tool.infoMessage("加密后Hex:");
		tool.infoMessage(encHex);
		tool.infoMessage("加密后Base64:");
		tool.infoMessage(encBase64);
	}

	@Action(displayName = "解密", order = 2, visible = false)
	public void decrypt(Tool tool) throws Exception {
		tool.clearMessages();
		SM2 sm2 = new SM2(privateKey, null);
		setMode(sm2);
		byte[] data = sm2.decrypt(encryptedContent, KeyType.PrivateKey);
		String text = new String(data, "UTF-8");
		tool.infoMessage("解密后文本内容:");
		tool.infoMessage(text);
	}

	@Action(displayName = "签名", order = 3, visible = false)
	public void sign(Tool tool) throws Exception {
		tool.clearMessages();
		SM2 sm2 = new SM2(privateKey, null);
		setMode(sm2);
		byte[] data = content.getBytes("UTF-8");
		byte[] signatureData = sm2.sign(data);
		String signatureHex = HexUtil.encodeHexStr(signatureData);
		String signatureBase64 = Base64.encode(signatureData);
		tool.infoMessage("签名值(Hex):");
		tool.infoMessage(signatureHex);
		tool.infoMessage("签名值(Base64):");
		tool.infoMessage(signatureBase64);
		signature = signatureHex;
	}

	@Action(displayName = "验证签名", order = 4, visible = false)
	public void verifySign(Tool tool) throws Exception {
		tool.clearMessages();
		SM2 sm2 = new SM2(null, publicKey);
		setMode(sm2);
		byte[] signatureData = SecureUtil.decode(signature);
		byte[] contentData = content.getBytes("UTF-8");
		boolean result = sm2.verify(contentData, signatureData);
		if (result) {
			tool.infoMessage("签名验证成功");
		} else {
			tool.warnMessage("签名验证失败");
		}
	}

	@Action(displayName = "生成密钥对", order = 9)
	public void generateKeyPair(Tool tool) throws Exception {
		tool.clearMessages();

		KeyPair keyPair = KeyUtil.generateKeyPair("SM2");
		byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
		byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
		String privateKeyHex = HexUtil.encodeHexStr(privateKeyBytes);
		String publicKeyHex = HexUtil.encodeHexStr(publicKeyBytes);
		String privateKeyBase64 = Base64.encode(privateKeyBytes);
		String publicKeyBase64 = Base64.encode(publicKeyBytes);
		SM2 sm2 = new SM2(keyPair.getPrivate(), keyPair.getPublic());
		String dHex = sm2.getDHex();
		String qHex = HexUtil.encodeHexStr(sm2.getQ(false));
		tool.infoMessage("私钥Hex:");
		tool.infoMessage(privateKeyHex);
		tool.infoMessage("公钥Hex:");
		tool.infoMessage(publicKeyHex);
		tool.infoMessage("私钥Base64:");
		tool.infoMessage(privateKeyBase64);
		tool.infoMessage("公钥Base64:");
		tool.infoMessage(publicKeyBase64);
		tool.infoMessage("私钥D值Hex(可供js库sm-crypto使用):");
		tool.infoMessage(dHex);
		tool.infoMessage("公钥Q值Hex(可供js库sm-crypto使用):");
		tool.infoMessage(qHex);

		this.privateKey = privateKeyBase64;
		this.publicKey = publicKeyBase64;
	}

	private void setMode(SM2 sm2) {
		if ("C1C3C2".equals(mode)) {
			sm2.setMode(Mode.C1C3C2);
		} else {
			sm2.setMode(Mode.C1C2C3);
		}
	}

}
